/**************************************************************************************
*
*	Copyright (C) 2009 - 2012 Brent Andrew O. Chua - See LICENSE.txt for more info.
*		
*	Filename	-	ComponentNativeScript.h
*
*	Description -	Component that can contain multiple instances in an actor.
*
*	Comments	-	This is what we use for creating other behaviors not defined in the 
*					default components
*
*	Modification History:
*			Name			   Date					Description
*			MrCodeSushi	-	06/15/2013	-	Creation of this file
**************************************************************************************/
#ifndef _PSX_COMPONENT_NATIVE_SCRIPT_H_
#define _PSX_COMPONENT_NATIVE_SCRIPT_H_

#include "PulseSTD.h"
#include "IComponent.h"
#include "StringID.h"
#include "FactoryNativeScript.h"

namespace Pulse
{
	class _INativeScript 
	{
	public:

		_INativeScript( ComponentNativeScript *pComponentOwner );

		virtual ~_INativeScript( void ) { }

	public:

		virtual  const CHAR * GetScriptName( void ) const = 0;

		virtual  const StringID * GetScriptNameStringID( void ) const = 0;

		virtual  const ULONG GetScriptNameID( void ) const = 0;

	protected:

		friend ComponentNativeScript;

		// Methods that the derived script classes needs to define
		///////////////////////////////////////////////////////////

		virtual EErrorCode::Type OnScriptInitialize( void ) = 0;
		
		virtual void OnScriptDestroy( void ) = 0;

		virtual void OnScriptUpdate( FLOAT dt ) = 0;

	protected:

		ComponentNativeScript * const m_pScriptComponent;
	};

	template < typename DerivedScriptType >
	class INativeScript : public _INativeScript
	{
	public:

		INativeScript( ComponentNativeScript *pComponentOwner )
			: _INativeScript( pComponentOwner )
		{
		}

		virtual ~INativeScript( void ) { }

	public:

		virtual  const CHAR * GetScriptName( void ) const sealed { return GetScriptNameStatic(); }

		virtual  const StringID * GetScriptNameStringID( void ) const sealed { return GetScriptNameStringIDStatic(); }

		virtual  const ULONG GetScriptNameID( void ) const sealed { return GetScriptNameIDStatic(); }

	public:

		// Methods that the derived script classes needs to define
		///////////////////////////////////////////////////////////
		
		// Each derived native script class should have a unique name.
		static const CHAR * GetScriptNameStatic( void ) { return m_scriptTypeID.GetString(); }

		// NOTE: Define a static StringID for each derived class
		static const StringID * GetScriptNameStringIDStatic( void ) { return &m_scriptTypeID; }

		// NOTE: Define a static StringID for each derived class
		static const ULONG GetScriptNameIDStatic( void ) { return m_scriptTypeID.GetID(); }

	protected:

		friend ComponentNativeScript;

		// Methods that the derived script classes needs to define
		///////////////////////////////////////////////////////////

		virtual EErrorCode::Type OnScriptInitialize( void ) = 0;

		virtual void OnScriptDestroy( void ) = 0;

		virtual void OnScriptUpdate( FLOAT dt ) = 0;

	protected:

		// NOTE: Every derived native script class needs to define this static data
		static const StringID m_scriptTypeID;

	};

	class ComponentNativeScript : public IComponent
	{
	public:

		ComponentNativeScript( Actor *pOwner );

		virtual ~ComponentNativeScript( void );

		virtual EErrorCode::Type Initialize( XMLElementPtr pElem );

		virtual const EComponent::Type GetType( void ) const { return EComponent::NATIVE_SCRIPT; }

		virtual const CHAR * GetName( void ) const { return PSX_String("Native Script"); }

		virtual void Update( FLOAT dt );

		virtual BOOL IsUnique( void ) { return FALSE; }

		template < typename NativeScriptType >
		void AttachScript( void );

		void RemoveScript( void );

	private:

		friend _INativeScript;

		// pImpl!!!
		_INativeScript *m_pNativeScript;

	};

	template < typename NativeScriptType >
	PSX_INLINE void ComponentNativeScript::AttachScript( void )
	{
		RemoveScript();
		_INativeScript *pNewScript = FactoryNativeScript::GetInstance()->Create( NativeScriptType::GetScriptNameIDStatic(), this );

		m_pNativeScript = pNewScript;

		m_pNativeScript->OnScriptInitialize();
	}
}

#endif /* _PSX_COMPONENT_NATIVE_SCRIPT_H_ */